package per.qiao.simplerest.proxy;


import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ObjectUtils;
import per.qiao.simplerest.anno.*;
import per.qiao.simplerest.components.Actuator;
import per.qiao.simplerest.core.RestServiceSupport;
import per.qiao.simplerest.exception.*;
import per.qiao.simplerest.restenum.RestMethod;
import per.qiao.simplerest.restresolver.RestInvokerMethod;
import per.qiao.simplerest.restargument.RestParameter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.IntStream;

/**
 * Company: 上海数惠系统技术有限公司
 * Department: 数据中心
 * Date: 2020-05-30 14:08
 *
 * @Author: qyp
 * Email: qiaoyp@dist.com.cn
 * Desc:
 */
public class RestMethodInterceptor implements MethodInterceptor {

    private Actuator actuator;

    public RestMethodInterceptor(Actuator actuator) {
        this.actuator = actuator;
    }

    /**
     * @param proxyObj cglib生成的代理对象
     * @param method 被代理的方法
     * @param args 方法参数
     * @param methodProxy 代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("当前调用方法: " + method.getName());
        // Object object = methodProxy.invokeSuper(proxyObj, args);
//        Annotation methodAnnotation = method.getAnnotation(RestMapping.class);

        Object[] restMethodAndAnnotationValue = getMethodAnnotationValue(method);
        //
        RestMapping classAnnotation = AnnotationUtils.findAnnotation(proxyObj.getClass(), RestMapping.class);

        String uri = mergeAnno(classAnnotation, (String) restMethodAndAnnotationValue[1]/*methodAnnotation.value()*/);
        if (ObjectUtils.isEmpty(uri)) {
            throw new RestServiceUriMissingException();
        }

        List<RestInvokerMethod> restInvokerMethods = RestServiceSupport.uriMethods.get(uri);
        if (ObjectUtils.isEmpty(restInvokerMethods)) {
            throw new RestServiceUriNotExistsException();
        }
        RestMethod restMethod = (RestMethod) restMethodAndAnnotationValue[0];
        RestInvokerMethod matchRestInvokerMethod = match(restMethod, restInvokerMethods);
        if (matchRestInvokerMethod == null) {
            throw new RestServiceUriNotExistsException();
        }
//        Method proxyMethod = ReflectionUtils.findMethod(proxyObj.getClass(), method.getName(), String.class);
//        ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
//        Stream.of(discoverer.getParameterNames(proxyMethod)).forEach(System.out::println);

        RestParameter[] restParameter = matchRestInvokerMethod.getRestParameter();
        fullRestParameter(restParameter, args);
        return actuator.execute(matchRestInvokerMethod);
    }

    private Object[] getMethodAnnotationValue(Method method) {

        Annotation[] annotations = method.getAnnotations();
        if (ObjectUtils.isEmpty(annotations)) {
            throw new RestServcieNoInvokMethodException();
        }
        Object[] restMethodAndAnnotationValue = new Object[2];
        if (AnnotatedElementUtils.hasAnnotation(method, RestGetMapping.class)) {
            restMethodAndAnnotationValue[0] = RestMethod.GET;
            restMethodAndAnnotationValue[1] = method.getAnnotation(RestGetMapping.class).value();
        } else if (AnnotatedElementUtils.hasAnnotation(method, RestPostMapping.class)) {
            restMethodAndAnnotationValue[0] = RestMethod.POST;
            restMethodAndAnnotationValue[1] = method.getAnnotation(RestPostMapping.class).value();
        } else if (AnnotatedElementUtils.hasAnnotation(method, RestPutMapping.class)) {
            restMethodAndAnnotationValue[0] = RestMethod.PUT;
            restMethodAndAnnotationValue[1] = method.getAnnotation(RestPutMapping.class).value();
        } else if (AnnotatedElementUtils.hasAnnotation(method, RestDeleteMapping.class)) {
            restMethodAndAnnotationValue[0] = RestMethod.DELETE;
            restMethodAndAnnotationValue[1] = method.getAnnotation(RestDeleteMapping.class).value();
        } else {
            throw new RestServiceNotSupportMethodException();
        }
        return restMethodAndAnnotationValue;
    }

    /**
     * 给参数填值
     *
     * @param restParameter
     * @param args
     */
    private void fullRestParameter(RestParameter[] restParameter, Object[] args) {
        if (ObjectUtils.isEmpty(restParameter)) {
            return;
        }
        if (restParameter.length != args.length) {
            throw new RestArgumentsMissingException();
        }
        IntStream.range(0, restParameter.length)
                .forEach(i -> restParameter[i].setValue(args[i]));
    }

    private String mergeAnno(RestMapping classAnnotation, String methodAnnoValue) {
        if (classAnnotation == null || "".equals(classAnnotation.value().trim())) {
            return methodAnnoValue;
        }
        String classAnnoValue = classAnnotation.value();
        if (classAnnoValue.endsWith("/")) {
            classAnnoValue = classAnnoValue.substring(0, classAnnoValue.length() - 2);
        }
        if (!methodAnnoValue.startsWith("/")) {
            methodAnnoValue += "/";
        }
        return classAnnoValue + methodAnnoValue;
    }

    private RestInvokerMethod match(RestMethod restMethod, List<RestInvokerMethod> restInvokerMethods) {
        for (RestInvokerMethod restInvokerMethod : restInvokerMethods) {
            RestMethod method = restInvokerMethod.getRestMethod();
            if (method == restMethod) {
                return restInvokerMethod;
            }
        }
        return null;
    }
}
